package com.naiterui.ehp.bs.payment.service.impl;

import java.util.Date;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.google.common.collect.Maps;
import com.naiterui.ehp.bp.bo.payment.NotifyBusinessRequestBO;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.support.exception.ResponseVO;
import com.naiterui.ehp.bp.support.utils.JsonMapper;
import com.naiterui.ehp.bp.utils.biz.ScheduleUtils;
import com.naiterui.ehp.bp.utils.date.DateUtil;
import com.naiterui.ehp.bp.utils.net.RestHttpUtil;
import com.naiterui.ehp.bs.payment.constant.PayConstants;
import com.naiterui.ehp.bs.payment.exception.NotifyException;
import com.naiterui.ehp.bs.payment.service.IOrderStatusService;
import com.naiterui.ehp.bs.payment.service.IRecoverNotifyBusinessService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 重试通知业务系统实现
 *
 * @author 王泽浩
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RecoverNotifyBusinessServiceImpl implements IRecoverNotifyBusinessService {
    private final IOrderStatusService orderStatusService;

    @Value("${ehp.domain.pay}")
    private String payHost;

    @Value("${ehp.domain.schedule}")
    private String scheduleHost;

    @Override
    public void notifyBusiness(NotifyBusinessRequestBO notifyBusinessRequest) {
        String orderSn = notifyBusinessRequest.getOrderSn();
        String refundOrderSn = notifyBusinessRequest.getRefundOrderSn();
        Integer notifyBusinessType = notifyBusinessRequest.getNotifyBusinessType();
        String notifyBusinessUrl = notifyBusinessRequest.getNotifyBusinessUrl();
        if (StringUtils.isAnyBlank(orderSn, notifyBusinessUrl)) {
            log.warn("回调参数不正确 orderSn:{} notifyBusinessUrl:{}", orderSn, notifyBusinessUrl);
            return;
        }
        log.info("开始回调业务系统 orderSn:{} 通知类型:{}", orderSn, notifyBusinessType);
        String body = notifyBusinessRequest.getBody();
        String data;
        try {
            data = RestHttpUtil.post().url(notifyBusinessUrl).body(body).executeData(new ParameterizedTypeReference<ResponseVO<String>>() {
            });
        } catch (BusinessException e) {
            throw new NotifyException(String.format("同步业务系统订单状态失败 orderSn:%s", orderSn));
        }
        if (!StringUtils.contains(data.toUpperCase(), PayConstants.RESULT_CODE_SUCCESS)) {
            log.error("回调业务系统\n【订单号】：{} \n【请求地址】：{}\n【请求数据】：\n{}\n【响应数据】：\n{}\n", orderSn, notifyBusinessUrl, body, data);
            throw new NotifyException(String.format("同步业务系统订单状态失败 orderSn:%s", orderSn));
        }
        log.info("业务回调成功 orderSn:{}", orderSn);
        switch (notifyBusinessType) {
            case NotifyBusinessRequestBO.NOTIFY_TYPE_PAY:
                this.orderStatusService.updatePayStatus(orderSn, NotifyBusinessRequestBO.NOTIFY_STATUS_SUCCESS);
                break;
            case NotifyBusinessRequestBO.NOTIFY_TYPE_REFUND:
                this.orderStatusService.updateRefundStatus(refundOrderSn, NotifyBusinessRequestBO.NOTIFY_STATUS_SUCCESS);
                break;
            default:
                throw new IllegalStateException("通知类型不正确");
        }

    }

    @Async
    @Override
    @Retryable(value = NotifyException.class, maxAttempts = 5, backoff = @Backoff(multiplier = 2, random = true))
    public void asyncNotifyBusiness(NotifyBusinessRequestBO notifyBusinessRequest) {
        this.notifyBusiness(notifyBusinessRequest);
    }

    /**
     * 重试失败后，增加重试任务，支付系统不在处理
     *
     * @param notifyException 重试失败异常
     */
    @SuppressWarnings("unused")
    @Recover
    public void recoverNotifyException(NotifyException notifyException, NotifyBusinessRequestBO notifyBusinessRequest) {
        log.error(notifyException.getMessage(), notifyException);
        String orderSn = notifyBusinessRequest.getOrderSn();
        Integer notifyBusinessType = notifyBusinessRequest.getNotifyBusinessType();
        int recoverTime = notifyBusinessRequest.getRecoverTime();
        if (recoverTime >= 10) {
            switch (notifyBusinessType) {
                case NotifyBusinessRequestBO.NOTIFY_TYPE_PAY:
                    this.orderStatusService.updatePayStatus(orderSn, NotifyBusinessRequestBO.NOTIFY_STATUS_FAILURE);
                    break;
                case NotifyBusinessRequestBO.NOTIFY_TYPE_REFUND:
                    this.orderStatusService.updateRefundStatus(notifyBusinessRequest.getRefundOrderSn(),
                                                               NotifyBusinessRequestBO.NOTIFY_STATUS_FAILURE);
                    break;
                default:
                    throw new IllegalStateException("通知类型不正确");
            }
            return;
        }
        log.info("业务通知失败，定时任务重试调用，{}", notifyBusinessRequest);
        notifyBusinessRequest.setRecoverTime(notifyBusinessRequest.getRecoverTime() + 1);
        final String paymentRecoverUrl = this.payHost + "/recover/notify";
        try {
            String invokeTime = DateUtil.formatDate(DateUtil.addMinutes(new Date(), 5), DateUtil.CRON_TIME_FORMAT);
            Map<String, String> paramMap = Maps.newHashMap();
            paramMap.put("body", JsonMapper.toJson(notifyBusinessRequest));
            ScheduleUtils.addCommonTask(this.scheduleHost, invokeTime, System.currentTimeMillis(), paymentRecoverUrl, paramMap);
        } catch (Exception e) {
            log.error("添加回调业务系统重试失败 {}", notifyBusinessRequest, e);
        }
    }

}
